package lumis.service.pagepersonalization.widgetmanipulationaction;

import lumis.content.ContentManagerFactory;
import lumis.portal.PortalException;
import lumis.portal.authentication.SessionConfig;
import lumis.portal.manager.ManagerException;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.manager.ManagerFactoryInternal;
import lumis.portal.preferences.IPreferences;
import lumis.portal.presentation.perspective.IPortalMode;
import lumis.portal.serviceinterfaceinstance.ServiceInterfaceInstanceConfig;
import lumis.portal.transaction.ITransaction;
import lumis.service.pagepersonalization.PagePersonalizationUtil;
import lumis.service.pagepersonalization.bean.PagePersonalizationHolderConfiguration;
import lumis.service.pagepersonalization.bean.PagePersonalizationHolderConfigurationPK;
import lumis.service.pagepersonalization.bean.Widget;
import lumis.service.pagepersonalization.bean.PagePersonalizationHolderConfiguration.Type;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * An {@link AbstractWidgetManipulationAction} implementation for moving user
 * widgets. Requires the following parameters:
 * <ul>
 * <li><b>wci</b>: The widget content identifier.</li>
 * <li><b>shi</b>: The source holder service interface instance identifier.</li>
 * <li><b>dhi</b>: The destination holder service interface instance identifier.
 * </li>
 * <li><b>p</b>: The position (zero based) in which the widget has been moved
 * into the destination holder.</li>
 * </ul>
 * The source and destination holders may or may not be the same.
 *
 * @version $Revision: 13246 $ $Date: 2011-07-14 17:08:26 -0300 (Thu, 14 Jul 2011) $
 * @since 6.0.0
 */
public class MoveWidgetToHolderAction extends AbstractWidgetManipulationAction
{
	private static final String INTERFACE_INSTANCE_ID = "ii";
	private static final String SOURCE_HOLDER_INSTANCE_ID = "shi";
	private static final String DESTINATION_HOLDER_INSTANCE_ID = "dhi";
	private static final String POSITION = "p";
	private static final String[] REQUIRED_PARAMETERS = new String[] { INTERFACE_INSTANCE_ID, SOURCE_HOLDER_INSTANCE_ID, DESTINATION_HOLDER_INSTANCE_ID, POSITION };

	/**
	 * Moves a widget in "normal" mode (not in edit mode).
	 *
	 * @param request
	 *            the request.
	 * @param sourceServiceInterfaceInstanceId
	 *            the source holder service interface instance identifier.
	 * @param destinationServiceInterfaceInstanceId
	 *            the destination holder service interface instance identifier.
	 * @param transaction
	 * @return a HTTP status code.
	 * @since 6.0.0
	 */
	private int moveInNormalMode(HttpServletRequest request, String sourceServiceInterfaceInstanceId,
			String destinationServiceInterfaceInstanceId, ITransaction transaction) throws PortalException
	{
		String widgetInterfaceInstanceId = getParameter(INTERFACE_INSTANCE_ID, request);

		Widget widget = Widget.getByInterfaceInstanceId(widgetInterfaceInstanceId);
		if (widget == null)
			return HttpServletResponse.SC_BAD_REQUEST;

		String widgetContentId = ContentManagerFactory.getContentManager().getContentVersionByItemId(widget.getId())
				.getContentLocale().getContent().getId();

		SessionConfig sessionConfig = SessionConfig.getCurrentSessionConfig();
		if (!(destinationServiceInterfaceInstanceId.equals(sourceServiceInterfaceInstanceId)))
		{
			Lock clusterLock = ManagerFactory.getLockManager().getLock("lumis.pagepersonalization.widgetholder.preferences#"+sessionConfig.getUserId());
			
			try
			{
				clusterLock.lock();
				// moving from a holder instance to other
				// get current widgets in source interface instance
				IPreferences preferencesSource = ManagerFactory.getPreferencesManager().getPreferences(sessionConfig,
						sourceServiceInterfaceInstanceId, IPreferences.TYPE_SERVICE_INTERFACE_INSTANCE);
	
				String[] valuesSource = preferencesSource.getValues(PagePersonalizationUtil.WIDGETS_PREFERENCES_NAME, null);
				if (valuesSource == null)
				{
					valuesSource = PagePersonalizationUtil.getDefaultWidgets(sessionConfig, 
							sourceServiceInterfaceInstanceId, Type.Default, transaction);
				}
				if (valuesSource == null || valuesSource.length == 0)
					return HttpServletResponse.SC_BAD_REQUEST;
	
				// remove the new widget from source interface instance
				List<String> valuesListSource = new ArrayList<String>(valuesSource.length);
				Collections.addAll(valuesListSource, valuesSource);
				if (!valuesListSource.remove(widgetContentId))
					return HttpServletResponse.SC_BAD_REQUEST;
				valuesSource = valuesListSource.toArray(new String[valuesListSource.size()]);
	
				// get current widgets...
				IPreferences preferences = ManagerFactory.getPreferencesManager().getPreferences(sessionConfig,
						destinationServiceInterfaceInstanceId, IPreferences.TYPE_SERVICE_INTERFACE_INSTANCE);
	
				String[] values = preferences.getValues(PagePersonalizationUtil.WIDGETS_PREFERENCES_NAME, null);
				if (values == null)
				{
					values = PagePersonalizationUtil.getDefaultWidgets(sessionConfig, 
							destinationServiceInterfaceInstanceId, Type.Default, transaction);
				}
				if (values == null)
					values = new String[0];
	
				// add the new widget to destination interface instance
				List<String> valuesList = new ArrayList<String>(values.length + 1);
				Collections.addAll(valuesList, values);
				if (valuesList.contains(widgetContentId))
					return HttpServletResponse.SC_BAD_REQUEST;
				valuesList.add(Integer.parseInt(getParameter(POSITION, request)), widgetContentId);
				values = valuesList.toArray(new String[valuesList.size()]);
	
				try
				{
					// FIXME: Both preferences should be commited together (in the
					// same transaction). Verify how to do it.
					preferencesSource.setValues(PagePersonalizationUtil.WIDGETS_PREFERENCES_NAME, valuesSource);
					preferences.setValues(PagePersonalizationUtil.WIDGETS_PREFERENCES_NAME, values);
					preferencesSource.store();
					preferences.store();
				}
				catch (Exception e)
				{
					return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
				}
				
				try
				{
					storePreferencesOtherHolders(sourceServiceInterfaceInstanceId, destinationServiceInterfaceInstanceId, transaction, sessionConfig);
				}
				catch (Exception e)
				{
					return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
				}
			}
			finally
			{
				clusterLock.unlock();
			}
			return HttpServletResponse.SC_OK;
		}
		else
		{
			Lock clusterLock = ManagerFactory.getLockManager().getLock("lumis.pagepersonalization.widgetholder.preferences#"+sessionConfig.getUserId());
			
			try
			{
				clusterLock.lock();
	
				// moving inside the same holder
				// get current widgets in interface instance
				IPreferences preferences = ManagerFactory.getPreferencesManager().getPreferences(sessionConfig,
						sourceServiceInterfaceInstanceId, IPreferences.TYPE_SERVICE_INTERFACE_INSTANCE);
	
				String[] values = preferences.getValues(PagePersonalizationUtil.WIDGETS_PREFERENCES_NAME,
						PagePersonalizationUtil.getDefaultWidgets(sessionConfig,
								sourceServiceInterfaceInstanceId, Type.Default, transaction));
	
				if (values == null || values.length == 0)
					return HttpServletResponse.SC_BAD_REQUEST;
	
				// remove the widget from interface instance
				List<String> valuesList = new ArrayList<String>(values.length);
				Collections.addAll(valuesList, values);
				if (!valuesList.remove(widgetContentId))
					return HttpServletResponse.SC_BAD_REQUEST;
				
				// add the widget to interface instance
				if (valuesList.contains(widgetContentId))
					return HttpServletResponse.SC_BAD_REQUEST;
				valuesList.add(Integer.parseInt(getParameter(POSITION, request)), widgetContentId);
				values = valuesList.toArray(new String[valuesList.size()]);
	
				try
				{
					preferences.setValues(PagePersonalizationUtil.WIDGETS_PREFERENCES_NAME, values);
					preferences.store();
				}
				catch (Exception e)
				{
					return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
				}
				
				try
				{
					storePreferencesOtherHolders(sourceServiceInterfaceInstanceId, destinationServiceInterfaceInstanceId, transaction, sessionConfig);
				}
				catch (Exception e)
				{
					return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
				}
			}
			finally
			{
				clusterLock.unlock();
			}
			return HttpServletResponse.SC_OK;
		}
	}

	/**
	 * Stores preferences to the other holders on the page except sourceHolder and destinationHolder.
	 * 
	 * @param sourceServiceInterfaceInstanceId id of the source holder. 
	 * @param destinationServiceInterfaceInstanceId id of the destination holder.
	 * @param transaction transaction used to the persist information on the database.
	 * @param sessionConfig the current session of the user
	 * @since %LUM_NEXT_VERSION%
	 */
	private void storePreferencesOtherHolders(String sourceServiceInterfaceInstanceId, String destinationServiceInterfaceInstanceId, ITransaction transaction, SessionConfig sessionConfig)
			throws ManagerException, PortalException
	{
		IPreferences preferences;
		String[] values;
		// stores preferences to the other holders on the page except sourceHolder and destinationHolder.
		// gets other holders ids of the page.
		String pageId = ManagerFactoryInternal.getServiceInterfaceInstanceManager().get(sessionConfig, sourceServiceInterfaceInstanceId, transaction).getPageId();
		Collection<String> serviceInterfaceInstanceIds = ManagerFactory.getServiceInterfaceInstanceManager().getIdsByPageId(sessionConfig, pageId, transaction);
		for (String id : serviceInterfaceInstanceIds)
		{
			ServiceInterfaceInstanceConfig serviceInterfaceInstanceConfig = ManagerFactoryInternal.getServiceInterfaceInstanceManager().get(sessionConfig, id, transaction);
			if (serviceInterfaceInstanceConfig.getServiceInterfaceId().equalsIgnoreCase(LUMIS_SERVICE_PAGEPERSONALIZATION_WIDGET_HOLDER_INTERFACE_ID) && !sourceServiceInterfaceInstanceId.equalsIgnoreCase(id) && !destinationServiceInterfaceInstanceId.equalsIgnoreCase(id))
			{
				// get current widgets...
				preferences = ManagerFactory.getPreferencesManager().getPreferences(sessionConfig,
						id, IPreferences.TYPE_SERVICE_INTERFACE_INSTANCE);

				values = preferences.getValues(PagePersonalizationUtil.WIDGETS_PREFERENCES_NAME, null);
				if (values == null)
				{
					values = PagePersonalizationUtil.getDefaultWidgets(sessionConfig,
							id, Type.Default, transaction);
				}
				
				if (values == null)
					values = new String[] {};

				try
				{
					preferences.setValues(PagePersonalizationUtil.WIDGETS_PREFERENCES_NAME, values);
					preferences.store();
				}
				catch (Exception e)
				{
					throw new PortalException (e.getMessage(), e);
				}
			}
		}
	}

	/**
	 * Moves a widget in edit mode.
	 *
	 * @param request
	 *            the request.
	 * @param sourceServiceInterfaceInstanceId
	 *            the source holder service interface instance identifier.
	 * @param destinationServiceInterfaceInstanceId
	 *            the destination holder service interface instance identifier.
	 * @return a HTTP status code.
	 * @throws PortalException
	 * @since 6.0.0
	 */
	private int moveInEditMode(HttpServletRequest request, String sourceServiceInterfaceInstanceId,
			String destinationServiceInterfaceInstanceId) throws PortalException
	{
		String widgetInterfaceInstanceId = getParameter(INTERFACE_INSTANCE_ID, request);
		Widget widget = Widget.getByInterfaceInstanceId(widgetInterfaceInstanceId);
		if (widget == null)
			return HttpServletResponse.SC_BAD_REQUEST;

		String widgetContentId = ContentManagerFactory.getContentManager().getContentVersionByItemId(widget.getId())
				.getContentLocale().getContent().getId();

		if (!(destinationServiceInterfaceInstanceId.equals(sourceServiceInterfaceInstanceId)))
		{
			// moving from a holder instance to other
			// get current widgets...
			PagePersonalizationHolderConfiguration tempHolderConfig = PagePersonalizationHolderConfiguration.getByTypeAndHolderInstanceId(Type.Temporary,
					destinationServiceInterfaceInstanceId);

			// add the widget to the destination interface instance...
			if (tempHolderConfig == null)
			{
				// in this case, position must be 0...
				if (Integer.parseInt(getParameter(POSITION, request)) != 0)
					return HttpServletResponse.SC_BAD_REQUEST;

				tempHolderConfig = new PagePersonalizationHolderConfiguration();
				PagePersonalizationHolderConfigurationPK pk = new PagePersonalizationHolderConfigurationPK();
				pk.setHolderInstanceId(destinationServiceInterfaceInstanceId);
				pk.setTypeOfConf(Type.Temporary.getSqlType());
				tempHolderConfig.setId(pk);

				tempHolderConfig.setWidgets(widgetContentId);
			}
			else
			{
				String[] widgets = tempHolderConfig.expand();
				int pos = Integer.parseInt(getParameter(POSITION, request));
				List<String> list = new ArrayList<String>(widgets.length + 1);
				Collections.addAll(list, widgets);
				if (list.contains(widgetContentId))
					return HttpServletResponse.SC_BAD_REQUEST;
				list.add(pos, widgetContentId);
				tempHolderConfig.setWidgets(list);
			}

			// remove the widget from source interface instance
			PagePersonalizationHolderConfiguration source = PagePersonalizationHolderConfiguration.getByTypeAndHolderInstanceId(Type.Temporary,
					sourceServiceInterfaceInstanceId);
			if (source == null)
				return HttpServletResponse.SC_BAD_REQUEST;

			String[] widgets = source.expand();
			List<String> list = new ArrayList<String>(widgets.length);
			Collections.addAll(list, widgets);
			if (!list.remove(widgetContentId))
				return HttpServletResponse.SC_BAD_REQUEST;
			if (list.size() > 0)
				source.setWidgets(list);
			else
				source.setWidgets((String) null);

			ManagerFactory.getEntityManager().persist(tempHolderConfig);
			return HttpServletResponse.SC_OK;
		}
		else
		{
			// moving inside the same holder
			// get current widgets...
			PagePersonalizationHolderConfiguration tempHolderConfig = PagePersonalizationHolderConfiguration.getByTypeAndHolderInstanceId(Type.Temporary,
					destinationServiceInterfaceInstanceId);

			// add the widget to the destination interface instance...
			if (tempHolderConfig == null)
				return HttpServletResponse.SC_BAD_REQUEST;

			String[] widgets = tempHolderConfig.expand();
			int pos = Integer.parseInt(getParameter(POSITION, request));
			List<String> list = new ArrayList<String>(widgets.length);
			Collections.addAll(list, widgets);
			if (!list.remove(widgetContentId))
				return HttpServletResponse.SC_BAD_REQUEST;
			if (list.contains(widgetContentId))
				return HttpServletResponse.SC_BAD_REQUEST;
			list.add(pos, widgetContentId);
			tempHolderConfig.setWidgets(list);

			return HttpServletResponse.SC_OK;
		}
	}

	@Override
	String[] getRequiredParameters()
	{
		return REQUIRED_PARAMETERS;
	}

	@Override
	int processWidgetManipulation(HttpServletRequest request, IPortalMode portalMode, ITransaction transaction) throws PortalException
	{
		try
		{
			if (Integer.parseInt(getParameter(POSITION, request)) < 0)
				return HttpServletResponse.SC_BAD_REQUEST;
		}
		catch (NumberFormatException e)
		{
			return HttpServletResponse.SC_BAD_REQUEST;
		}

		String sourceServiceInterfaceInstanceId = getParameter(SOURCE_HOLDER_INSTANCE_ID, request);
		SessionConfig sessionConfig = SessionConfig.getCurrentSessionConfig();
		ServiceInterfaceInstanceConfig sourceHolder = ManagerFactory.getServiceInterfaceInstanceManager().get(sessionConfig, sourceServiceInterfaceInstanceId, transaction);
		
		String destinationServiceInterfaceInstanceId = getParameter(DESTINATION_HOLDER_INSTANCE_ID, request);
		ServiceInterfaceInstanceConfig destinationHolder = ManagerFactory.getServiceInterfaceInstanceManager().get(sessionConfig, destinationServiceInterfaceInstanceId, transaction);
		
		if (!sourceHolder.getServiceInstanceId().equals(destinationHolder.getServiceInstanceId()))
			return HttpServletResponse.SC_BAD_REQUEST;
		
		if (portalMode.getId() == IPortalMode.MODE_PERSONALIZATION)
		{
			// check permission for managing holder
			checkManageHolderPermission(destinationHolder.getServiceInstanceId());
			
			return moveInEditMode(request, sourceServiceInterfaceInstanceId, destinationServiceInterfaceInstanceId);
		}
		else
		{
			// check permission for personalizing page
			checkPersonalizePagePermission(destinationHolder.getServiceInstanceId());
			
			return moveInNormalMode(request, sourceServiceInterfaceInstanceId, destinationServiceInterfaceInstanceId,
					transaction);
		}
	}
}
